/*
 * Copyright 2009 StructBeans Project
 * 
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 
 * License for the specific language governing rights and limitations under 
 * the License.
 */

package org.structbeans.core;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

public class BeanInfo<T> {

	private static Logger logger = Logger.getLogger(BeanInfo.class);
	
	private Class<T> clazz;
	private List<Attribute> attributes;
	private ByteOrder byteOrder;
	
	public BeanInfo(Class<T> clazz) {
		this.clazz = clazz;
		attributes = new LinkedList<Attribute>();
	}

	public Class<T> getClazz() {
		return clazz;
	}

	public void setClazz(Class<T> clazz) {
		this.clazz = clazz;
	}

	public List<Attribute> getAttributes() {
		return attributes;
	}

	public void setAttributes(List<Attribute> attributes) {
		this.attributes = attributes;
	}
	
	public ByteOrder getByteOrder() {
		return byteOrder;
	}

	public void setByteOrder(ByteOrder byteOrder) {
		this.byteOrder = byteOrder;
	}

	public void validate() {
		// Step 0 is verify attributes exist		
		if (attributes.size() == 0)
			throw new ValidationException("Class of type: " +  clazz.getName() + " does not contain any mapped attributes.");
		
		// Step 1 is to verify there are no missing or duplicated indices
		Collections.sort(attributes, new Comparator<Attribute>(){

			public int compare(Attribute o1, Attribute o2) {
				if (o1.getIndex() < o2.getIndex())
					return -1;
				else if (o1.getIndex() > o2.getIndex())
					return 1;
				else 
					throw new ValidationException("Attributes have the same index value: " + o1.getName() + "," + o2.getName() + "=" + o1.getIndex());				
			}});
		
		int expected = 0;
		for (Attribute attribute : attributes) {
			if (attribute.getIndex() != expected)
				throw new ValidationException("Attribute index values were not valid. Expected index of " + expected + " but got " + attribute.getIndex());
			expected++;
		}
		
		// Step 2 is to verify the attribute has accessible (public) getters and setters
		for (Attribute attribute : attributes) {
			
			String expectedGetter = attribute.getExpectedGetterName();
			String expectedSetter = attribute.getExpectedSetterName();
			
			try {
				
				if (logger.isDebugEnabled())
					logger.debug(this.toString() + ",validate - Checking for getter: " + expectedGetter + ":" + attribute.getAttributeClass().getName() + " in class " + clazz.getName());
								
				Method method = clazz.getMethod(expectedGetter, (Class<?>[])null);
				
				if (method.getReturnType() != attribute.getAttributeClass()) {
					throw new ValidationException("Could not find getter method " + attribute.getAttributeClass().getName() + " " + expectedGetter + " in class " + clazz.getName());
				}
				
			} catch (SecurityException e) {
				throw new ValidationException("Could not find getter method " + expectedGetter + ":" + attribute.getAttributeClass().getName() + " in class " + clazz.getName(), e);
			} catch (NoSuchMethodException e) {					
				throw new ValidationException("Could not find getter method " + expectedGetter + ":"+ attribute.getAttributeClass().getName() + " in class " + clazz.getName(), e);
			}
			
			try {
				
				if (logger.isDebugEnabled())
					logger.debug(this.toString() + ",validate - Checking for setter: " + expectedSetter + "(" + attribute.getAttributeClass().getName() + ") in class " + clazz.getName());
				
				@SuppressWarnings("unused")
				Method method = clazz.getMethod(expectedSetter, attribute.getAttributeClass()); 
			} catch (SecurityException e) {
				throw new ValidationException("Could not find getter method " + expectedGetter + "(" + attribute.getAttributeClass().getName() + ") in class " + clazz.getName(), e);
			} catch (NoSuchMethodException e) {					
				throw new ValidationException("Could not find getter method " + expectedGetter + "(" + attribute.getAttributeClass().getName() + ") in class " + clazz.getName(), e);
			}
		}		
	}
}
